feat(career): 職務経歴書 保存時の文章校正(誤字脱字・表記ゆれ)#309
Conversation
保存確認ダイアログ(CareerDiffModal)に「校正の指摘」セクションを追加し、 編集中フォームの全テキスト項目を textlint + prh で校正して表示する。 保存はブロックせず警告のみ。指摘は右サイドバー下部に青系・控えめで表示する。 - 校正エンジンは Web Worker(src/proofread/)でフロント完結。textlint kernel + 技術文書プリセット全23ルール + prh(IT表記ゆれ辞書同梱)。個人情報を外部送信しない。 - kuromoji 辞書(public/kuromoji-dict/)は静的配信。辞書ロード失敗時は形態素解析依存 ルールを除外して prh + 非依存ルールで継続(グレースフルデグラデーション)。 - useProofread フックがダイアログ表示中だけ校正を起動。初回保存(新規・差分無し)も 空フォーム基準の差分算出によりダイアログを開き、校正の取りこぼしを防ぐ。 - worker のブラウザ統合: window/process 最小ポリフィル、node 組込(path/os/assert)の ブラウザ実装エイリアス、辞書 .gz を二重 gzip させない配信(dev プラグイン / _headers)。 - ADR-0009 を追加。
- 左右 diff の編集中ペインで、校正指摘のあるフィールド(data-fp 一致)に青の波線を 付与(diffHighlight の annotateHtml/foldUnchanged を拡張)。差分の背景色(黄/緑/赤)と 重ねても潰れないよう下線で表現し、指摘のある項目は折りたたみから除外する。 - 変更点リストと校正リストの並びを PDF レイアウト順(氏名→職務要約→職務経歴→資格→自己PR) に統一。buildCareerChanges / collectCareerTexts で self_pr を末尾へ移動し、左右ペインと サイドバーの縦順を一致させて突合しやすくした。
ユーザーレビューを受けた改善: - 校正指摘を左右ペイン(PDFレイアウト)にも青の波線でマーキング。差分の背景色 (黄/緑/赤)と重ねても潰れないよう下線で表現。校正指摘のある項目は折りたたみから除外。 - 変更点・校正の並び順を PDF レイアウト順(氏名→職務要約→職歴→資格→自己PR)に統一。 buildCareerChanges / collectCareerTexts の self_pr を末尾へ移動。 - 右サイドバーの「変更点」「校正」2セクション分割を廃止し、フィールド単位の 1本のレビュー一覧(buildReviewEntries)に統合。各カードに差分と校正を併記し、 左右ペインと縦順を一致させて上から突合できるようにした。
|
Need an answer fast? Review this PR in Change Stack to ask focused questions about the PR or a changed range. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThis PR adds end-to-end Japanese text proofreading to the career form save flow. When users save their résumé, a Web Worker runs textlint with Japanese technical-writing rules to detect spelling and style issues, displays them in the save-confirmation dialog alongside diffs, and does not block saving. The implementation includes graceful fallback if kuromoji dictionary resources fail to load. ChangesFrontend proofreading via Web Worker
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
frontend/e2e/career-proofread.spec.ts (1)
59-71: ⚡ Quick winUse SSoT/test constants instead of inline Japanese message literals in selectors.
Lines 59–71 directly embed UI strings (
"例: 山田 太郎","変更内容の確認","変更点・校正","この内容で保存"). Please route these through a shared message source (or stable test IDs) so copy updates don’t silently break E2E.As per coding guidelines, “In frontend code, follow message management rules … message literals are forbidden and must use Single Source of Truth (SSoT)”.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/e2e/career-proofread.spec.ts` around lines 59 - 71, The test embeds UI message literals directly in selectors (page.getByPlaceholder("例: 山田 太郎"), page.getByRole(..., { name: "変更内容の確認" }), page.getByText("変更点・校正"), page.getByRole(..., { name: "この内容で保存" })), violating the SSoT rule; update the spec to import and use the shared message/test constants (or stable test IDs) instead of inline Japanese strings for those calls, replacing the literal arguments with the appropriate SSoT constants (or test-id lookups) so UI text changes won’t break frontend/e2e/career-proofread.spec.ts.Source: Coding guidelines
frontend/src/utils/careerReview.ts (1)
56-76: 💤 Low valueConsider extracting magic numbers to named constants.
The values
400(line 59) and500(line 75) are used to rank unknown fields for stable sorting. For maintainability, consider extracting these as named constants.♻️ Suggested refactor
+/** Unknown top-level fields are sorted after known fields, using charCode for stability. */ +const UNKNOWN_TOP_LEVEL_BASE = 400; +/** Unknown container fields are sorted after known fields within their container. */ +const UNKNOWN_CONTAINER_FIELD_BASE = 500; + /** 既知の並びにあればその index、無ければ末尾側(文字コードで安定ソート)に寄せる。 */ function orderIndex(list: string[], seg: string): number { const i = list.indexOf(seg); - return i >= 0 ? i : 400 + (seg.charCodeAt(0) || 0); + return i >= 0 ? i : UNKNOWN_TOP_LEVEL_BASE + (seg.charCodeAt(0) || 0); } /** * パスを「並び順を表す数値タプル」に変換する。 * - 先頭セグメント: トップレベル順 * - 数値セグメント: 配列 index * - 名前付きセグメント: 直近の配列名(2 つ前)から決まるコンテナのフィールド順 */ function rankTuple(path: string): number[] { const segs = path.split("."); return segs.map((seg, i) => { if (i === 0) return orderIndex(TOP_ORDER, seg); if (/^\d+$/.test(seg)) return Number(seg); const containerName = i >= 2 && /^\d+$/.test(segs[i - 1]) ? segs[i - 2] : null; const order = containerName ? CONTAINER_FIELD_ORDER[containerName] : null; - return order ? orderIndex(order, seg) : 500; + return order ? orderIndex(order, seg) : UNKNOWN_CONTAINER_FIELD_BASE; }); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/utils/careerReview.ts` around lines 56 - 76, The code uses magic numbers 400 and 500 in orderIndex and rankTuple; extract them into named constants (e.g., UNKNOWN_TOP_ORDER_BASE = 400 and UNKNOWN_FIELD_ORDER = 500) and replace the literal usages in orderIndex (return i >= 0 ? i : UNKNOWN_TOP_ORDER_BASE + (seg.charCodeAt(0) || 0)) and in rankTuple’s default return (return order ? orderIndex(order, seg) : UNKNOWN_FIELD_ORDER) so the intent is clear and easy to maintain; also add brief comments above the new constants explaining their purpose.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/adr/0009-frontend-textlint-proofread.md`:
- Line 25: ADR text and implementation disagree on the number of
morphology-dependent rules excluded on dictionary-load failure; update the ADR
to match the worker implementation by changing “10ルール” to “11ルール” (or explicitly
list the rules) so it reflects the actual KUROMOJI_RULE_IDS set (which currently
includes "arabic-kanji-numbers"); refer to the KUROMOJI_RULE_IDS identifier and
the "arabic-kanji-numbers" rule when making the ADR edit to ensure counts and/or
listed rules align.
In `@frontend/src/components/forms/CareerDiffModal.module.css`:
- Line 287: Replace the deprecated CSS declaration "word-break: break-word" in
CareerDiffModal.module.css with the modern equivalent; locate both occurrences
of the exact token "word-break: break-word" and change them to "overflow-wrap:
break-word" (or to "word-break: break-all" only if you need more aggressive
breaking), ensuring the replacement preserves existing selectors/styles that
contain these declarations.
In `@frontend/vite.config.ts`:
- Around line 27-28: The middleware builds filePath using
normalize(join(publicDir, url)) which fails when url starts with "/" because
join treats it as absolute; update the construction to ensure the url is treated
as relative (e.g., strip the leading slash or prefix with "./") before joining
so filePath correctly begins with publicDir, then keep the existing
startsWith(publicDir) check; locate the filePath variable creation and change
the normalize(join(publicDir, url)) expression (and any related checks) to use a
relative-safe join such as joining publicDir with url.slice(1) or '.' + url so
requests under "/kuromoji-dict/" are not treated as absolute paths.
---
Nitpick comments:
In `@frontend/e2e/career-proofread.spec.ts`:
- Around line 59-71: The test embeds UI message literals directly in selectors
(page.getByPlaceholder("例: 山田 太郎"), page.getByRole(..., { name: "変更内容の確認" }),
page.getByText("変更点・校正"), page.getByRole(..., { name: "この内容で保存" })), violating
the SSoT rule; update the spec to import and use the shared message/test
constants (or stable test IDs) instead of inline Japanese strings for those
calls, replacing the literal arguments with the appropriate SSoT constants (or
test-id lookups) so UI text changes won’t break
frontend/e2e/career-proofread.spec.ts.
In `@frontend/src/utils/careerReview.ts`:
- Around line 56-76: The code uses magic numbers 400 and 500 in orderIndex and
rankTuple; extract them into named constants (e.g., UNKNOWN_TOP_ORDER_BASE = 400
and UNKNOWN_FIELD_ORDER = 500) and replace the literal usages in orderIndex
(return i >= 0 ? i : UNKNOWN_TOP_ORDER_BASE + (seg.charCodeAt(0) || 0)) and in
rankTuple’s default return (return order ? orderIndex(order, seg) :
UNKNOWN_FIELD_ORDER) so the intent is clear and easy to maintain; also add brief
comments above the new constants explaining their purpose.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 7d82d332-d0d8-4587-be80-c8dfede552a5
⛔ Files ignored due to path filters (13)
frontend/package-lock.jsonis excluded by!**/package-lock.jsonfrontend/public/kuromoji-dict/base.dat.gzis excluded by!**/*.gzfrontend/public/kuromoji-dict/cc.dat.gzis excluded by!**/*.gzfrontend/public/kuromoji-dict/check.dat.gzis excluded by!**/*.gzfrontend/public/kuromoji-dict/tid.dat.gzis excluded by!**/*.gzfrontend/public/kuromoji-dict/tid_map.dat.gzis excluded by!**/*.gzfrontend/public/kuromoji-dict/tid_pos.dat.gzis excluded by!**/*.gzfrontend/public/kuromoji-dict/unk.dat.gzis excluded by!**/*.gzfrontend/public/kuromoji-dict/unk_char.dat.gzis excluded by!**/*.gzfrontend/public/kuromoji-dict/unk_compat.dat.gzis excluded by!**/*.gzfrontend/public/kuromoji-dict/unk_invoke.dat.gzis excluded by!**/*.gzfrontend/public/kuromoji-dict/unk_map.dat.gzis excluded by!**/*.gzfrontend/public/kuromoji-dict/unk_pos.dat.gzis excluded by!**/*.gz
📒 Files selected for processing (26)
docs/adr/0009-frontend-textlint-proofread.mdfrontend/e2e/career-proofread.spec.tsfrontend/package.jsonfrontend/public/_headersfrontend/src/components/forms/CareerDiffModal.module.cssfrontend/src/components/forms/CareerDiffModal.tsxfrontend/src/components/forms/CareerResumeForm.tsxfrontend/src/constants/messages.tsfrontend/src/hooks/career/useProofread.test.tsfrontend/src/hooks/career/useProofread.tsfrontend/src/proofread/collectCareerTexts.test.tsfrontend/src/proofread/collectCareerTexts.tsfrontend/src/proofread/issueFormat.test.tsfrontend/src/proofread/issueFormat.tsfrontend/src/proofread/prh-it-terms.ymlfrontend/src/proofread/proofread.worker.tsfrontend/src/proofread/proofreadClient.tsfrontend/src/proofread/textlint-modules.d.tsfrontend/src/proofread/types.tsfrontend/src/proofread/worker-env-polyfill.tsfrontend/src/utils/careerDiff.tsfrontend/src/utils/careerReview.test.tsfrontend/src/utils/careerReview.tsfrontend/src/utils/diffHighlight.test.tsfrontend/src/utils/diffHighlight.tsfrontend/vite.config.ts
- ADR-0009: 辞書ロード失敗時に除外する形態素依存ルール数を 10→11 に修正 (KUROMOJI_RULE_IDS は arabic-kanji-numbers を含む実装と整合)。 - CareerDiffModal.module.css: 非推奨の word-break: break-word を overflow-wrap: break-word に統一(stylelint 指摘)。 - vite.config.ts: 辞書配信ミドルウェアで先頭スラッシュを除去してから join し、 publicDir 相対解決を明示(join の絶対パス扱いを防御。挙動は従来どおり)。 - careerReview.ts: 並び順ランクのマジックナンバー(400/500)を名前付き定数化。
概要
職務経歴書の保存確認ダイアログ(
CareerDiffModal)に 文章校正(誤字脱字・表記ゆれ・冗長表現) を追加します。保存前に編集中フォームの全テキスト項目を校正し、PDF レイアウトと同じ並びで指摘を表示します。警告のみで保存はブロックしません。転職で人の目に触れる職務経歴書に、提出前のセルフチェック手段を提供するのが目的です。
方針(ADR-0009)
@textlint/kernel+textlint-rule-preset-ja-technical-writing(全23ルール)+prh(IT 用語の表記ゆれ辞書を同梱)。frontend/public/kuromoji-dict/に静的配信。辞書ロード失敗時は形態素解析依存ルールを除外して prh + 非依存ルールで継続(グレースフルデグラデーション)。UI
useProofread)。新規作成の初回保存でもダイアログを開き、取りこぼしを防ぐ。buildReviewEntries)。PDF レイアウト順(氏名→職務要約→職歴→資格→自己PR)に並べ、左右ペインと縦順を一致させて上から突合できる。主な変更
frontend/src/proofread/(worker / client / collectCareerTexts / issueFormat / prh辞書 / polyfill)、hooks/career/useProofread.ts、utils/careerReview.ts、public/kuromoji-dict/・public/_headers。CareerDiffModal/CareerResumeForm/diffHighlight.ts/careerDiff.ts/messages.ts/vite.config.tsを更新。ブラウザ統合のポイント(worker でのハマりどころ)
window/process(cwd/nextTick) の最小ポリフィル、node 組込(path/os/assert)のブラウザ実装エイリアス。path.joinが://を潰す問題回避)。.gzを二重 gzip 展開させない配信(dev: Vite プラグイン / prod:public/_headers)。検証
make ci相当を pass: ESLint /lint-frontend-messages/ ユニット 295 pass(新規テスト追加)/ build。npm audit --audit-level=high: high/critical なし。career-proofread.spec.tsで 実ブラウザ → worker → kuromoji辞書ロード → prh指摘表示まで検証)。留意点
public/にコミットしています(リポジトリ履歴が増加。ADR-0009 にトレードオフと将来の CDN 移行条件を記載)。https://claude.ai/code/session_01QANbvc5WkCDYDNyZw8gUnx
Generated by Claude Code
Summary by CodeRabbit